/*
 * Copyright (C) 2004-2006, 2008, 2014-2016 Apple Inc. All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public License
 * along with this library; see the file COPYING.LIB.  If not, write to
 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
 * Boston, MA 02110-1301, USA.
 *
 */

#ifndef AtomicString_h
#define AtomicString_h

#include <utility>
#include "AtomicStringImpl.h"
#include "IntegerToStringConversion.h"
#include "WTFString.h"

// Define 'NO_IMPLICIT_ATOMICSTRING' before including this header,
// to disallow (expensive) implicit String-->AtomicString conversions.
#ifdef NO_IMPLICIT_ATOMICSTRING
#define ATOMICSTRING_CONVERSION explicit
#else
#define ATOMICSTRING_CONVERSION
#endif

namespace WTF {

    struct AtomicStringHash;

    class AtomicString {
    public:
        WTF_EXPORT_PRIVATE static void init();

        AtomicString();

        AtomicString(const LChar *);

        AtomicString(const char *);

        AtomicString(const LChar *, unsigned length);

        AtomicString(const UChar *, unsigned length);

        AtomicString(const UChar *, unsigned length, unsigned existingHash);

        AtomicString(const UChar *);

        template<size_t inlineCapacity>
        explicit AtomicString(const Vector<UChar, inlineCapacity> &characters)
                : m_string(AtomicStringImpl::add(characters.data(), characters.size())) {
        }

        AtomicString(AtomicStringImpl *);

        AtomicString(RefPtr<AtomicStringImpl> &&);

        ATOMICSTRING_CONVERSION AtomicString(StringImpl *);

        ATOMICSTRING_CONVERSION AtomicString(const String &);

        AtomicString(StringImpl *baseString, unsigned start, unsigned length);

        // FIXME: AtomicString doesn’t always have AtomicStringImpl, so one of those two names needs to change..
        AtomicString(UniquedStringImpl *uid);

        enum ConstructFromLiteralTag {
            ConstructFromLiteral
        };

        AtomicString(const char *characters, unsigned length, ConstructFromLiteralTag)
                : m_string(AtomicStringImpl::addLiteral(characters, length)) {
        }

        template<unsigned charactersCount>
        ALWAYS_INLINE AtomicString(const char (&characters)[charactersCount], ConstructFromLiteralTag)
                : m_string(AtomicStringImpl::addLiteral(characters, charactersCount - 1)) {
            COMPILE_ASSERT(charactersCount > 1, AtomicStringFromLiteralNotEmpty);
            COMPILE_ASSERT((charactersCount - 1 <= ((unsigned(~0) - sizeof(StringImpl)) / sizeof(LChar))), AtomicStringFromLiteralCannotOverflow);
        }

        // We have to declare the copy constructor and copy assignment operator as well, otherwise
        // they'll be implicitly deleted by adding the move constructor and move assignment operator.
        AtomicString(const AtomicString &other) : m_string(other.m_string) {}

        AtomicString(AtomicString &&other) : m_string(WTFMove(other.m_string)) {}

        AtomicString &operator=(const AtomicString &other) {
            m_string = other.m_string;
            return *this;
        }

        AtomicString &operator=(AtomicString &&other) {
            m_string = WTFMove(other.m_string);
            return *this;
        }

        // Hash table deleted values, which are only constructed and never copied or destroyed.
        AtomicString(WTF::HashTableDeletedValueType) : m_string(WTF::HashTableDeletedValue) {}

        bool isHashTableDeletedValue() const { return m_string.isHashTableDeletedValue(); }

        unsigned existingHash() const { return isNull() ? 0 : impl()->existingHash(); }

        operator const String &() const { return m_string; }

        const String &string() const { return m_string; };

        AtomicStringImpl *impl() const { return static_cast<AtomicStringImpl *>(m_string.impl()); }

        bool is8Bit() const { return m_string.is8Bit(); }

        const LChar *characters8() const { return m_string.characters8(); }

        const UChar *characters16() const { return m_string.characters16(); }

        unsigned length() const { return m_string.length(); }

        UChar operator[](unsigned int i) const { return m_string[i]; }

        WTF_EXPORT_STRING_API static AtomicString number(int);

        WTF_EXPORT_STRING_API static AtomicString number(unsigned);

        WTF_EXPORT_STRING_API static AtomicString number(unsigned long);

        WTF_EXPORT_STRING_API static AtomicString number(unsigned long long);

        WTF_EXPORT_STRING_API static AtomicString number(double);
        // If we need more overloads of the number function, we can add all the others that String has, but these seem to do for now.

        bool contains(UChar c) const { return m_string.contains(c); }

        bool contains(const LChar *s, bool caseSensitive = true) const { return m_string.contains(s, caseSensitive); }

        bool contains(const String &s) const { return m_string.contains(s); }

        bool contains(const String &s, bool caseSensitive) const { return m_string.contains(s, caseSensitive); }

        bool containsIgnoringASCIICase(const String &s) const { return m_string.containsIgnoringASCIICase(s); }

        size_t find(UChar c, unsigned start = 0) const { return m_string.find(c, start); }

        size_t find(const LChar *s, unsigned start = 0, bool caseSentitive = true) const { return m_string.find(s, start, caseSentitive); }

        size_t find(const String &s, unsigned start = 0, bool caseSentitive = true) const { return m_string.find(s, start, caseSentitive); }

        size_t findIgnoringASCIICase(const String &s) const { return m_string.findIgnoringASCIICase(s); }

        size_t findIgnoringASCIICase(const String &s, unsigned startOffset) const { return m_string.findIgnoringASCIICase(s, startOffset); }

        size_t find(CharacterMatchFunctionPtr matchFunction, unsigned start = 0) const { return m_string.find(matchFunction, start); }

        bool startsWith(const String &s) const { return m_string.startsWith(s); }

        bool startsWithIgnoringASCIICase(const String &s) const { return m_string.startsWithIgnoringASCIICase(s); }

        bool startsWith(const String &s, bool caseSensitive) const { return m_string.startsWith(s, caseSensitive); }

        bool startsWith(UChar character) const { return m_string.startsWith(character); }

        template<unsigned matchLength>
        bool startsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const {
            return m_string.startsWith<matchLength>(prefix, caseSensitive);
        }

        bool endsWith(const String &s) const { return m_string.endsWith(s); }

        bool endsWithIgnoringASCIICase(const String &s) const { return m_string.endsWithIgnoringASCIICase(s); }

        bool endsWith(const String &s, bool caseSensitive) const { return m_string.endsWith(s, caseSensitive); }

        bool endsWith(UChar character) const { return m_string.endsWith(character); }

        template<unsigned matchLength>
        bool endsWith(const char (&prefix)[matchLength], bool caseSensitive = true) const {
            return m_string.endsWith<matchLength>(prefix, caseSensitive);
        }

        WTF_EXPORT_STRING_API AtomicString convertToASCIILowercase() const;

        WTF_EXPORT_STRING_API AtomicString convertToASCIIUppercase() const;

        int toInt(bool *ok = 0) const { return m_string.toInt(ok); }

        double toDouble(bool *ok = 0) const { return m_string.toDouble(ok); }

        float toFloat(bool *ok = 0) const { return m_string.toFloat(ok); }

        bool percentage(int &p) const { return m_string.percentage(p); }

        bool isNull() const { return m_string.isNull(); }

        bool isEmpty() const { return m_string.isEmpty(); }

#if USE(CF)
        AtomicString(CFStringRef);
#endif
#ifdef __OBJC__
        AtomicString(NSString*);
        operator NSString*() const { return m_string; }
#endif

        // AtomicString::fromUTF8 will return a null string if
        // the input data contains invalid UTF-8 sequences.
        static AtomicString fromUTF8(const char *, size_t);

        static AtomicString fromUTF8(const char *);

#ifndef NDEBUG
        void show() const;
#endif

    private:
        // The explicit constructors with AtomicString::ConstructFromLiteral must be used for literals.
        AtomicString(ASCIILiteral);

        enum class CaseConvertType {
            Upper, Lower
        };

        template<CaseConvertType>
        AtomicString convertASCIICase() const;

        WTF_EXPORT_STRING_API static AtomicString fromUTF8Internal(const char *, const char *);

        String m_string;
    };

    static_assert(sizeof(AtomicString) == sizeof(String), "AtomicString and String must be same size!");

    inline bool operator==(const AtomicString &a, const AtomicString &b) { return a.impl() == b.impl(); }

    bool operator==(const AtomicString &, const LChar *);

    inline bool operator==(const AtomicString &a, const char *b) { return WTF::equal(a.impl(), reinterpret_cast<const LChar *>(b)); }

    inline bool operator==(const AtomicString &a, const Vector<UChar> &b) { return a.impl() && equal(a.impl(), b.data(), b.size()); }

    inline bool operator==(const AtomicString &a, const String &b) { return equal(a.impl(), b.impl()); }

    inline bool operator==(const LChar *a, const AtomicString &b) { return b == a; }

    inline bool operator==(const String &a, const AtomicString &b) { return equal(a.impl(), b.impl()); }

    inline bool operator==(const Vector<UChar> &a, const AtomicString &b) { return b == a; }

    inline bool operator!=(const AtomicString &a, const AtomicString &b) { return a.impl() != b.impl(); }

    inline bool operator!=(const AtomicString &a, const LChar *b) { return !(a == b); }

    inline bool operator!=(const AtomicString &a, const char *b) { return !(a == b); }

    inline bool operator!=(const AtomicString &a, const String &b) { return !equal(a.impl(), b.impl()); }

    inline bool operator!=(const AtomicString &a, const Vector<UChar> &b) { return !(a == b); }

    inline bool operator!=(const LChar *a, const AtomicString &b) { return !(b == a); }

    inline bool operator!=(const String &a, const AtomicString &b) { return !equal(a.impl(), b.impl()); }

    inline bool operator!=(const Vector<UChar> &a, const AtomicString &b) { return !(a == b); }

    bool equalIgnoringASCIICase(const AtomicString &, const AtomicString &);

    bool equalIgnoringASCIICase(const AtomicString &, const String &);

    bool equalIgnoringASCIICase(const String &, const AtomicString &);

    bool equalIgnoringASCIICase(const AtomicString &, const char *);

    template<unsigned length>
    bool equalLettersIgnoringASCIICase(const AtomicString &, const char (&lowercaseLetters)[length]);

    inline AtomicString::AtomicString() {
    }

    inline AtomicString::AtomicString(const LChar *s)
            : m_string(AtomicStringImpl::add(s)) {
    }

    inline AtomicString::AtomicString(const char *s)
            : m_string(AtomicStringImpl::add(s)) {
    }

    inline AtomicString::AtomicString(const LChar *s, unsigned length)
            : m_string(AtomicStringImpl::add(s, length)) {
    }

    inline AtomicString::AtomicString(const UChar *s, unsigned length)
            : m_string(AtomicStringImpl::add(s, length)) {
    }

    inline AtomicString::AtomicString(const UChar *s, unsigned length, unsigned existingHash)
            : m_string(AtomicStringImpl::add(s, length, existingHash)) {
    }

    inline AtomicString::AtomicString(const UChar *s)
            : m_string(AtomicStringImpl::add(s)) {
    }

    inline AtomicString::AtomicString(AtomicStringImpl *imp)
            : m_string(imp) {
    }

    inline AtomicString::AtomicString(RefPtr<AtomicStringImpl> &&imp)
            : m_string(WTFMove(imp)) {
    }

    inline AtomicString::AtomicString(StringImpl *imp)
            : m_string(AtomicStringImpl::add(imp)) {
    }

    inline AtomicString::AtomicString(const String &s)
            : m_string(AtomicStringImpl::add(s.impl())) {
    }

    inline AtomicString::AtomicString(StringImpl *baseString, unsigned start, unsigned length)
            : m_string(AtomicStringImpl::add(baseString, start, length)) {
    }

    inline AtomicString::AtomicString(UniquedStringImpl *uid)
            : m_string(uid) {
    }

#if USE(CF)
    inline AtomicString::AtomicString(CFStringRef s)
        :  m_string(AtomicStringImpl::add(s))
    {
    }
#endif

#ifdef __OBJC__
    inline AtomicString::AtomicString(NSString* s)
        : m_string(AtomicStringImpl::add((__bridge CFStringRef)s))
    {
    }
#endif

// Define external global variables for the commonly used atomic strings.
// These are only usable from the main thread.
#ifndef ATOMICSTRING_HIDE_GLOBALS
    extern const WTF_EXPORTDATA AtomicString nullAtom;
    extern const WTF_EXPORTDATA AtomicString emptyAtom;
    extern const WTF_EXPORTDATA AtomicString starAtom;
    extern const WTF_EXPORTDATA AtomicString xmlAtom;
    extern const WTF_EXPORTDATA AtomicString xmlnsAtom;

    inline AtomicString AtomicString::fromUTF8(const char *characters, size_t length) {
        if (!characters)
            return nullAtom;
        if (!length)
            return emptyAtom;
        return fromUTF8Internal(characters, characters + length);
    }

    inline AtomicString AtomicString::fromUTF8(const char *characters) {
        if (!characters)
            return nullAtom;
        if (!*characters)
            return emptyAtom;
        return fromUTF8Internal(characters, nullptr);
    }

#endif

// AtomicStringHash is the default hash for AtomicString
    template<typename T>
    struct DefaultHash;
    template<>
    struct DefaultHash<AtomicString> {
        typedef AtomicStringHash Hash;
    };

    template<unsigned length>
    inline bool equalLettersIgnoringASCIICase(const AtomicString &string, const char (&lowercaseLetters)[length]) {
        return equalLettersIgnoringASCIICase(string.string(), lowercaseLetters);
    }

    inline bool equalIgnoringASCIICase(const AtomicString &a, const AtomicString &b) {
        return equalIgnoringASCIICase(a.string(), b.string());
    }

    inline bool equalIgnoringASCIICase(const AtomicString &a, const String &b) {
        return equalIgnoringASCIICase(a.string(), b);
    }

    inline bool equalIgnoringASCIICase(const String &a, const AtomicString &b) {
        return equalIgnoringASCIICase(a, b.string());
    }

    inline bool equalIgnoringASCIICase(const AtomicString &a, const char *b) {
        return equalIgnoringASCIICase(a.string(), b);
    }

    template<>
    struct IntegerToStringConversionTrait<AtomicString> {
        using ReturnType = AtomicString;
        using AdditionalArgumentType = void;

        static AtomicString flush(LChar *characters, unsigned length, void *) { return {characters, length}; }
    };

} // namespace WTF

#ifndef ATOMICSTRING_HIDE_GLOBALS
using WTF::AtomicString;
using WTF::nullAtom;
using WTF::emptyAtom;
using WTF::starAtom;
using WTF::xmlAtom;
using WTF::xmlnsAtom;
#endif

#include <wtf/text/StringConcatenate.h>

#endif // AtomicString_h
